package org.example.webservice.interceptor;

import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.headers.Header;
import org.apache.cxf.helpers.DOMUtils;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.example.webservice.user.IUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.xml.namespace.QName;
import java.util.List;

/**
 * {@link IUserService} 出拦截器，用于设置账户密码进行登陆。
 * 1、通过在 <soap:Header> 下自定义标签元素进行验证。
 * 2、客户端必须必须按着服服务端的约定标签格式进行传输，比如标签名称等，否则服务端永远无法解析到值。
 *
 * @author wangMaoXiong
 * @version 1.0
 * @date 2023/10/29 10:08
 */
public class WsUserOutInterceptor extends AbstractPhaseInterceptor<SoapMessage> {

    private static final Logger log = LoggerFactory.getLogger(WsUserOutInterceptor.class);

    /**
     * webService 需要传递的账号与密码
     */
    private String name;
    private String password;

    /**
     * 使用构造器传入参数，构造器中必须使用 super 设置拦截器发生的时刻/阶段
     * org.apache.cxf.phase.Phase 中提供了大量的常量表示拦截器发生的时刻
     *
     * @param name
     * @param password
     */
    public WsUserOutInterceptor(String name, String password) {
        // 协议前进行拦截
        super(Phase.PRE_PROTOCOL);
        this.name = name;
        this.password = password;
    }

    /**
     * 默认情况下，客户端给服务端请求的 soap 消息如下：
     * <pre>
     * <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
     * 	<soap:Body>
     * 		<ns2:getStudentByIdResponse xmlns:ns2="http://web_service.lct.com/">
     * 			<getStudentById>
     * 				<birthday>2019-02-18T16:19:12.102+08:00</birthday>
     * 				<punishStatus>5</punishStatus>
     * 				<sID>11</sID>
     * 				<sName>0a90959c-22ee-497b-b60d-0550b1f2adf7</sName>
     * 			</getStudentById>
     * 		</ns2:getStudentByIdResponse>
     * 	</soap:Body>
     * </soap:Envelope>
     * </pre>
     * 其中的 <body></body>部分为请求的主体，现在为 Envelope 设置头信息 <head></head>，将账户密码信息放在 <head></head>中，<head>与 body 同级。
     * <head></head>中的内容设置为账户与密码元素，如：
     * <pre>
     * <head>
     * 	<userInfo>
     * 		<userName>admin</userName>
     * 		<password>123456</password>
     * 	</userInfo>
     * </head>
     * </pre>
     * 其中的 userInfo、name、password 等元素名称必须和服务端约定的一致，因为服务端还需要根据这些名称解析出元素的值。
     *
     * @param message ：在客户端请求服务端前，先进入此方法，然后将账户密码信息添加到 soap 消息头中
     */
    @Override
    public void handleMessage(SoapMessage message) {
        // 创建文档对象
        Document document = DOMUtils.createDocument();
        // 创建标签元素
        Element userInfoEle = document.createElement("userInfo");
        Element userNameEle = document.createElement("userName");
        Element passwordEle = document.createElement("password");
        // 设置标签元素内容
        userNameEle.setTextContent(this.name);
        passwordEle.setTextContent(this.password);
        // 添加子元素
        userInfoEle.appendChild(userNameEle);
        userInfoEle.appendChild(passwordEle);

        // org.apache.cxf.headers.Header 最后将创建好的 soap 头信息添加到 SoapMessage 中
        // QName 构造器中的值与后面的 userInfoEle 元素的标签名保持一致
        List<Header> headerList = message.getHeaders();
        headerList.add(new Header(new QName("userInfo"), userInfoEle));
    }

}
